package xmid

import (
	"errors"
	"sync"

	"gorm.io/gorm"
)

// 执行事务处理的中间数据依赖
type Context struct {
	db           sync.Map      // 数据库缓存[缓存gorm.DB类型]
	oth          sync.Map      // 其他数据存储[缓存/存储一些其他类型的数据]
	err          error         // 错误导出
	handlers     []HandlerFunc // 中间件列表
	index        int           // 当前执行的组数据
	maxLen       int           // 待执行的中间件最大数量
	hookStart    []HandlerFunc // 开始执行的钩子，在调用Run时会首先调用此处的钩子
	hookEnd      []HandlerFunc // 处理结束的钩子，会在调用Run结束时调用此处函数
	hookRollback []HandlerFunc // 发生错误回退时回调的钩子，会在调用Run结束时且发生err时调用此处的钩子
	hookCommit   []HandlerFunc // 事务提交时的钩子，会在调用Run结束时且未发生err时调用此处的钩子
}

// 设置使用过程中需要调用的一些其他数据
//
//	key	待设置的数据下标
//	val	待设置的数据值
func (c *Context) Set(key string, val any) {
	c.oth.Store(key, val)
}

// 获取使用过程中需要调用的一些其他数据
//
//	key	待获取的数据下标
func (c *Context) Get(key string) (any, error) {
	if v, ok := c.oth.Load(key); ok {
		if v == nil {
			return nil, errors.New("找到的数据值为空")
		}
		return v, nil
	}
	return nil, errors.New("数据未找到")
}

// 设置DB 中间件事务处理时使用
//
//	key	数据库下标，多数据库连接时使用
//	db	待设置的数据库连接
func (c *Context) SetDBS(key string, db *gorm.DB) {
	c.db.Store(key, db)
}

// 设置DB 中间件事务处理时使用[等同于调用c.SetDBS("default",xxx)]
//
//	db	待设置的数据库连接
func (c *Context) SetDB(db *gorm.DB) {
	c.SetDBS("default", db)
}

// 获取事务处理的数据库连接[等同于调用c.GetDBS("default")]
func (c *Context) GetDB() (*gorm.DB, error) {
	return c.GetDBS("default")
}

// 获取事务处理的数据库连接
//
//	key	数据库下标，多数据库连接时使用
func (c *Context) GetDBS(key string) (*gorm.DB, error) {
	if v, ok := c.db.Load(key); ok {
		switch db := v.(type) {
		case *gorm.DB:
			return db, nil
		default:
			c.db.Delete(key)
		}
	}
	return nil, errors.New("数据连接 " + key + " 不存在")
}

// 添加中间件支持
//
//	h	中间件处理过程
func (c *Context) Use(h ...HandlerFunc) *Context {
	c.handlers = append(c.handlers, h...)
	return c
}

// 开始运行时的钩子
//
//	h	开始运行时调用的函数
func (c *Context) HookStart(h ...HandlerFunc) *Context {
	c.hookStart = append(c.hookStart, h...)
	return c
}

// 运行结束时的钩子
//
//	h	运行结束时调用的函数
func (c *Context) HookEnd(h ...HandlerFunc) *Context {
	c.hookEnd = append(c.hookEnd, h...)
	return c
}

// 发生错误时的回退钩子
//
//	h	发生错误时调用的函数
func (c *Context) HookRollback(h ...HandlerFunc) *Context {
	c.hookRollback = append(c.hookRollback, h...)
	return c
}

// 未发生错误时的提交钩子
//
//	h	提交时调用的函数
func (c *Context) HookCommit(h ...HandlerFunc) *Context {
	c.hookCommit = append(c.hookCommit, h...)
	return c
}

// 执行运行结果
//
//	h	待运行函数
func (c *Context) Run(h HandlerFunc) error {
	if h != nil {
		c.handlers = append(c.handlers, h)
	}
	c.maxLen = len(c.handlers)
	for i := 0; i < len(c.hookStart); i++ {
		c.hookStart[i](c)
	}
	c.index = -1
	c.Next()
	if c.err == nil {
		for i := 0; i < len(c.hookCommit); i++ {
			c.hookCommit[i](c)
		}
	} else {
		for i := 0; i < len(c.hookRollback); i++ {
			c.hookRollback[i](c)
		}
	}
	for i := 0; i < len(c.hookEnd); i++ {
		c.hookEnd[i](c)
	}
	return c.err
}

// 执行下一个中间件
func (c *Context) Next() {
	c.index++
	// 如果中间出现过错误信息，就不再往下进行处理，直接将错误信息抛出到最外层
	if c.err != nil {
		c.Abort()
	}
	for c.index < c.maxLen {
		c.handlers[c.index](c)
		c.index++
	}
}

// 终止中间件的处理
// 此操作会终止中间件的执行，但是不会调用HookRollback钩子
func (c *Context) Abort() {
	c.index = abortConst
}

// 携带错误信息终止中间件的处理，但是不会调用HookCommit钩子
//
//	err	携带的错误信息
func (c *Context) AbortWithError(err error) {
	c.err = err
	c.index = abortConst
}

// 获取其中的错误信息
func (c *Context) Error() error {
	return c.err
}
